// ***************************************************************
//  CEZP2P.cpp   Create date: 09/16/2009
//  -------------------------------------------------------------
//  Author: Song Haifeng
//  
//  Version history: 
//	 	  
//  -------------------------------------------------------------
//  Copyright (C) 2009  Hooyou tech. - All Rights Reserved
// ***************************************************************
// 
// ***************************************************************
#include "stdafx.h"
#include "CEZP2P.h"

unsigned long CEZP2P::m_sSeqnum = 1;

EZNetInit g_EZNetInit;

BOOL CEZP2P::Send(char* p2pServer, USHORT port, char* channelID, int optType, PBYTE pData, int nDataLen, unsigned long &nSeq, int nTimeout )
{
	ASSERT(m_socket == INVALID_SOCKET);

	m_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (m_socket == INVALID_SOCKET)
		goto _exitErr;

	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(p2pServer);

	if (addr.sin_addr.s_addr == -1)
	{
		struct hostent *pHost;
		pHost = gethostbyname(p2pServer);
		if (pHost != NULL)
		{
			if (pHost->h_addr == NULL)
				goto _exitErr;
			addr.sin_addr.s_addr = ((struct in_addr *)pHost->h_addr)->s_addr;
		}
		else
			goto _exitErr;
	}
	addr.sin_port = htons(port);

	int timeout = 5*1000;// nTimeout;

	if (setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) == SOCKET_ERROR)
		goto _exitErr;

	if (setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)) == SOCKET_ERROR)
		goto _exitErr;
#if 0

	u_long arg = 1;
	if (ioctlsocket(m_socket, FIONBIO, &arg) != 0)
		goto _exitErr;



	DWORD dwStart = GetTickCount();
	int nRet = connect(m_socket, (SOCKADDR*)&addr, sizeof(addr));



	fd_set rfd;
	FD_ZERO(&rfd);
	FD_SET((unsigned int)m_socket, &rfd);

	timeval tm;
	tm.tv_sec = nTimeout;
	tm.tv_usec = 0;
	int nret = select(0,&rfd,NULL,NULL,&tm);
	if (nret == 0)
	{
		DWORD dwEnd = GetTickCount();
		g_log.Print(1, "timeout %d secs\n", (dwEnd - dwStart) /1000);
		goto _exitErr;
	}
	else if (nret == -1)
		goto _exitErr;

	DWORD dwEnd = GetTickCount();
	if (!FD_ISSET(m_socket,&rfd))
		goto _exitErr;

	g_log.Print(1, "%d secs\n", (dwEnd - dwStart) /1000);
	if (nRet == SOCKET_ERROR)
		goto _exitErr;

	arg = 0;
	if (ioctlsocket(m_socket, FIONBIO, &arg) != 0)
		goto _exitErr;
#else

	DWORD dwStart = GetTickCount();
	int nRet = connect(m_socket, (SOCKADDR*)&addr, sizeof(addr));
	DWORD dwEnd = GetTickCount();
	g_log.Print(1, "connect %d secs\n", (dwEnd - dwStart) /1000);
	if (nRet == SOCKET_ERROR)
		goto _exitErr;

#endif

	//
	char* buffer = new char[nDataLen + sizeof(EZLIVESTANDHEADER) + 256];
	int  buflen = nDataLen + sizeof(EZLIVESTANDHEADER);

	PEZLIVESTANDHEADER header = (PEZLIVESTANDHEADER)buffer;
	memset(header,0,sizeof(EZLIVESTANDHEADER));
	strncpy_s((char*)header->Signature,8, HY_SIGNATURE, 6);
	strncpy_s((char*)header->ChannelID,16,channelID,strlen(channelID));
	header->opttype = optType;
	nSeq = m_sSeqnum++;
	header->Version = VERSION_P2P;
	header->seqnum = nSeq;
	header->datasize = nDataLen;

	g_log.Print(1, "P2P Send: type=%d, seqnum=%d, data length=%d channelID=%s version=%d\n",
		header->opttype, header->seqnum, header->datasize, header->ChannelID,header->Version);

	memcpy(buffer + sizeof(EZLIVESTANDHEADER), pData, nDataLen);

	int nSendByes = send(m_socket,buffer,buflen,0);
	if (nSendByes == SOCKET_ERROR)
	{
		delete []buffer;
		goto _exitErr;
	}

	delete []buffer;

	g_log.Print(1, "P2P Send %d bytes\n", buflen);
	return TRUE;

_exitErr:
	int err = WSAGetLastError();
	g_log.Print(1, "P2P Send has a error! %d\n", err);
	closesocket(m_socket);
	m_socket = INVALID_SOCKET;
	return FALSE;
}

BOOL CEZP2P::Receive( PBYTE pBuf, int nBuflen, int nTimeout )
{
	if (m_socket == INVALID_SOCKET)
	{
		g_log.Print(1, "P2P Receive: socket is not valid\n");
		return FALSE;
	}

	PEZLIVESTANDHEADER header = (PEZLIVESTANDHEADER)pBuf;
	memset(header, 0, sizeof(EZLIVESTANDHEADER));

	int len = 0;
	int i = 0;

	fd_set read_fds;
	timeval tm;
	int count;

	while (len < sizeof(EZLIVESTANDHEADER))
	{
		
		while(true)
		{
			FD_ZERO(&read_fds);
			FD_SET((unsigned int)m_socket, &read_fds);

			tm.tv_sec = 0;
			tm.tv_usec = 1000*1000;
			count = select(0, &read_fds, NULL, NULL, &tm);

			if (count == 0)
			{
				i++;
				if (i < nTimeout)
				{
					g_log.Print(1, "++\n");
					continue;
				}				

				g_log.Print(1, "P2P Receive: receive data timeout\n");
				closesocket(m_socket);
				m_socket = INVALID_SOCKET;
				return FALSE;
			}
			else if (count < 0 || count > 2) 
			{
				g_log.Print(1, "P2P Receive: socket error in select()\n");
				goto _exitErr;
			}
			break;
		}


		if (FD_ISSET((unsigned int)m_socket, &read_fds)) 
		{
			int nlen = recv(m_socket,((char*)header) + len, sizeof(EZLIVESTANDHEADER) - len,0);
			if (nlen == SOCKET_ERROR)
			{
				g_log.Print(1, "P2P Receive: recv err\n");
				goto _exitErr;
			}
			else if (nlen == 0)
			{
				g_log.Print(1, "P2P Receive: server disconnected\n");
				closesocket(m_socket);
				m_socket = INVALID_SOCKET;
				return FALSE;
			}


			len += nlen;
			g_log.Print(1, "P2P Receive: recv %d bytes\n", len);
		}
	} 

	if (strncmp((char*)header->Signature,HY_SIGNATURE,6) == 0)
	{
		int datalen = header->datasize;
		char* buff = (char*)pBuf + sizeof(EZLIVESTANDHEADER);
		while (datalen > 0) 
		{			
			int bytes = recv(m_socket,(char*)buff, datalen,0);
			if (bytes > 0) 
			{
				buff += bytes;
				datalen -= bytes;
			} 
			else if (bytes == 0) 
			{
				g_log.Print(1, "P2P Receive: server disconnected\n");
				closesocket(m_socket);
				m_socket = INVALID_SOCKET;
				return FALSE;
			}
			else if (bytes == SOCKET_ERROR) 
				goto _exitErr;
		}
		g_log.Print(1, "P2P Receive: recv %d Response bytes\n", header->datasize);

	}
	else
	{
		g_log.Print(1, "P2P Receive: header is wrong\n");
		closesocket(m_socket);
		m_socket = INVALID_SOCKET;
		return FALSE;
	}

	closesocket(m_socket);
	m_socket = INVALID_SOCKET;
	return TRUE;

_exitErr:
	int err = WSAGetLastError();
	g_log.Print(1, "P2P Receive has a error %d\n", err);
	closesocket(m_socket);
	m_socket = INVALID_SOCKET;

	return FALSE;
}

long CEZP2P::StartWMS(char* p2pServer, USHORT port, char* channelID, WMSADDR* wmsAddr, int wmsSize, int nTimeout)
{
	BOOL bResult = FALSE;

	unsigned long nSeq = 0;
	bResult = Send(p2pServer, port, channelID, CLIENT_CMD_STARTMMS, (PBYTE)wmsAddr, sizeof(WMSADDR)*wmsSize, nSeq, nTimeout);

	if (!bResult)
		return -1;

	char buffer[1024*8] = {0};
	int len = sizeof(buffer);

	bResult = Receive((PBYTE)buffer, len, nTimeout);
	if (!bResult)
		return -1;

	PEZLIVESTANDHEADER header = (PEZLIVESTANDHEADER)buffer;
	if (header->opttype != CLIENT_RESPON_STARTMMS)
	{
		g_log.Print(1, "StartWMS: response type is wrong! %d\n", header->opttype);
		return -1;
	}

	if (header->acknum != nSeq)
	{
		g_log.Print(1, "StartWMS: response acknum is wrong!\n");
		return -1;
	}

	
	PRESPONSEBODY body = (PRESPONSEBODY)(buffer + sizeof(EZLIVESTANDHEADER));

	return body->respStatus;
}

long CEZP2P::StopWMS( char* p2pServer, USHORT port, char* channelID, WMSADDR* wmsAddr, int wmsSize, int nTimeout )
{
	BOOL bResult = FALSE;

	unsigned long nSeq = 0;
	bResult = Send(p2pServer, port, channelID, CLIENT_CMD_STOPMMS, (PBYTE)wmsAddr, sizeof(wmsAddr)*wmsSize, nSeq, nTimeout);

	if (!bResult)
		return -1;

	char buffer[1024*8] = {0};
	int len = sizeof(buffer);

	bResult = Receive((PBYTE)buffer, len, nTimeout);
	if (!bResult)
		return -1;

	PEZLIVESTANDHEADER header = (PEZLIVESTANDHEADER)buffer;
	if (header->opttype != CLIENT_RESPON_STOPMMS)
	{
		g_log.Print(1, "StopWMS: response type is wrong! %d\n", header->opttype);
		return -1;
	}

	if (header->acknum != nSeq)
	{
		g_log.Print(1, "StopWMS: response acknum is wrong!\n");
		return -1;
	}

	PRESPONSEBODY body = (PRESPONSEBODY)(buffer + sizeof(EZLIVESTANDHEADER));

	return body->respStatus;

}